/**
 * 实现方法流程定制化
 * @author hushaoli
 */


var  wxLog = require("./../log/log");

/**
 * 流程map
 * key: 不同流程的名字
 * value: 具体流程的实例（包括流程的执行方法和执行过程)
 */
var process_map = new Map();

/**
 * 流程过滤器map
 * key : 不同过滤器名称
 * value : 具体流程过滤器方法(用于筛选对应的流程)
 */
var filter_map = new Map();

/**
 * 增加一个新的流程
 * @param key : 流程名称
 * @return : 具体流程的实例（包括流程的执行方法和执行过程)
 */
function addProcess(key) {
  var preProcess = process_map.get(key);
  if (typeof preProcess == undefined || preProcess == undefined) {
    var tmp = this.create(key);
    process_map.set(key, tmp);
    return tmp;
  } else {
    return preProcess;
  }
}

/**
 * 流程选择器
 * @params filterFun : 选择器方法（通过选择器来选择对应的流程，返回结果为流程的名称）
 * @params params : 选择器方法的输入参数（可为空)
 */
function addFilter(key, filterFun, res) {
  var filterHandle = filter_map.get(key);
  if (typeof filterHandle == undefined || filterHandle == undefined) {
    filterHandle = {
      fHandle: filterFun,
      params: res,
      getProcess: function () {
        var returnKey = this.fHandle(this.params);
        return getProcess(returnKey);
      }
    };
    filter_map.set(key, filterHandle);
    return filterHandle;
  } else {

    return filterHandle;
  }
}

function getFilter(key) {
  return filter_map.get(key);
}

/**
 * 获取一个已有的流程
 * @param key : 流程名称
 * @return : 具体流程的实例（包括流程的执行方法和执行过程)
 */
function getProcess(key) {
  return process_map.get(key);
}

/**
 * 创建一个流程实例
 * @return 返回流程实例
 */
function create(process_name) {
  var process_info = {
    process_name: process_name,
    errDefault: undefined,
    timeFunction: undefined,
    sysTimeoutFunc: undefined, //解决在执行一个节点函数时，该节点由于等待监听回调而无法进行下一步操作的BUG
    finalFunction: undefined,
    timeout: 0,
    timeBegin: new Date(),
    processList: {}, //主流程函数存储结构
    processNameList: {}, //主流程函数名称存储结果（用于开发模式下的日志打印）
    errList: {}, //每个方法的错误函数
    preList: {}, //每个方法的预处理函数
    afterList: {}, //每个方法的后处理函数
    rejectList: {}, //每个方法对应的拒绝处理逻辑（调用该方法，触发拒绝逻辑，并终止流程)
    current_index: 0, //当前处理的函数位置
    size: 0, //当前流程方法个数
    isRunning: false, //运行状态 
    // isRunningKey: process_name + "_isRunning",
    redirectTo_url: undefined, //跳转地址
    redirectTo_type: undefined, //跳转方式
    env: "pro", //开发模式(pro: 线上模式; dev:开发模式[默认为pro模式])
    process_start_data: undefined,
    //process_data : undefined,
    //process_data_page : undefined,

    /**
     * 打印日志函数
     * @param {日志消息} msg 
     */
    log(msg) {
      if (this.env == "dev") {
        console.log(msg);
      } else {
        wxLog.log(msg, this.process_name);
      }
    },
    /**
     * 设置为开发模式
     * 用于开发模式，打印每个节点的输入参数和输出参数
     */
    dev() {
      this.env = "dev";
      return this;
    },
    /**
     * 增加流程方法
     */
    add: function (curr, curr_name) {
      this.processList[this.size] = curr;
      if (typeof curr_name == undefined || curr_name == undefined) {
        this.processNameList[this.size] = "Function_" + this.size;
      } else {
        this.processNameList[this.size] = curr_name;
      }
      this.size++;
      return this;
    },
    /**
     * 增加流程方法
     */
    add_err: function (errFunction) {
      this.errList[this.size] = errFunction;
      return this;
    },
    add_err_default: function (errFunction) {
      this.errDefault = errFunction;
      return this;
    },
    /**
     * 增加流程预处理方法
     */
    add_pre: function (preFunction) {
      this.preList[this.size - 1] = preFunction;
      return this;
    },
    /**
     * 增加流程后处理方法
     */
    add_after: function (afterFunction) {
      this.afterList[this.size - 1] = afterFunction;
      return this;
    },
    add_reject: function (rejectFunction) {
      this.rejectList[this.size - 1] = rejectFunction;
      return this;
    },
    /**
     * 判断当前流程是否在执行
     * @return isRunning : true-在运行中， false：未运行
     */
    get_state: function () {
      return this.isRunning;
    },
    set_state: function (state) {
      this.isRunning = state;
    },
    /**
     * 当前执行到哪一步
     */
    get_current_index: function () {
      return this.current_index;
    },
    /**
     * @param {最后的执行方法} fun 
     */
    finally: function (finalFun) {
      this.finalFunction = finalFun;
      return this;
    },
    /**
     * 重新走流程，但是定时器重置 
     */
    run_again(res) {
      /**
       * 初始化入参
       */
      if (typeof res == undefined || res == undefined) {
        res = this.process_start_data;
      }
      /**
       * 清理数据
       */
      this.finalize();
      /**
       * 初始化启动参数
       */
      this.current_index = 1;
      this.set_state(true);
      this.sysTimeoutFunc = undefined;

      //开发模式下的日志打印
      if (typeof res != undefined && res != undefined) {
        this.log("return data is ---------");
        this.log(res);
      }
      this.log("------rerun the process-------");
      this.log("开始调用方法:" + this.processNameList[this.current_index - 1]);
      try {
        /**
         * 判断是否需要预处理
         */
        var pre_res = this.preList[this.current_index - 1];
        if (typeof pre_res != undefined && pre_res) {
          res = pre_res(res);
        }
        /**
         * 开始执行主函数
         */
        var curr_res = this.processList[this.current_index - 1];
        if (typeof curr_res == undefined || curr_res == undefined) {
          this.log("本流程无节点函数");
          return;
        }
        return curr_res(res);
      } catch (e) {
        this.log("catch");
        this.log("流程异常, err:---------");
        this.log(e);
        return this.err_catch(e);
      }
    },
    /**
     * 开始流程，执行第一个方法
     */
    start: function (res) {
      this.process_start_data = res;
      this.log("current env:" + this.env);
      /**
       * 初始化启动参数
       */
      this.current_index = 1;
      this.timeBegin = new Date().getTime();
      // this.isRunning = true;
      this.set_state(true);
      this.sysTimeoutFunc = undefined;


      //开发模式下的日志打印
      if (typeof res != undefined && res != undefined ){
        this.log("return data is ---------");
        this.log(res);
      }
      this.log("------call first node -------");
      this.log("开始调用方法:" + this.processNameList[this.current_index - 1]);

      try {
        /**
         * 判断是否需要预处理
         */
        var pre_res = this.preList[this.current_index - 1];
        if (typeof pre_res != undefined && pre_res) {
          res = pre_res(res);
        }
        /**
         * 开始执行主函数
         */
        var curr_res = this.processList[this.current_index - 1];
        if (typeof curr_res == undefined || curr_res == undefined) {
          this.log("本流程无节点函数");
          return;
        }
        return curr_res(res);
      } catch (e) {
        return this.err_catch(e);
      }
    },

    /**
     * finalize
     */
    finalize: function () {
      this.log("finally, process use time [" + (new Date - this.timeBegin)+ "]ms") ;
      //清理定时任务
      if (typeof this.sysTimeoutFunc != undefined && this.sysTimeoutFunc != undefined) {
        clearTimeout(this.sysTimeoutFunc);
        this.sysTimeoutFunc = undefined;
      }
      
      //执行最终的数据清理函数
      if (typeof this.finalFunction != undefined && this.finalFunction != undefined) {
        this.finalFunction();
      }

      //判断是否为跳转操作
      if (typeof this.redirectTo_type != undefined && this.redirectTo_type != undefined) {
        this.log("结束流程页面跳转方式[" + this.redirectTo_type + "]跳转页面[" + this.redirectTo_url + "]");
        var pageTo = this.redirectTo_url;
        if (this.redirectTo_type == "reLaunch") {
          this.redirectTo_type = undefined;
          this.redirectTo_url = undefined;
          wx.reLaunch({
            url: pageTo,
          });
        }
        if (this.redirectTo_type == "redirectTo") {
          this.redirectTo_type = undefined;
          this.redirectTo_url = undefined;
          wx.redirectTo({
            url: pageTo,
          });
        }
        if (this.redirectTo_type == "navigateTo") {
          this.redirectTo_type = undefined;
          this.redirectTo_url = undefined;
          wx.navigateTo({
            url: pageTo,
          });
        }
        if (this.redirectTo_type == "navigateBack") {
          this.redirectTo_type = undefined;
          this.redirectTo_url = undefined;
          wx.navigateBack({
            url: pageTo,
          });
        }
      }
    },

    /**
     * 停止流程
     */
    stop: function () {
      if (!this.isRunning) {
       return false;
      }
      this.log("流程已经被手动结束");
      this.set_state(false);
      this.finalize();
      return true;
    },
    /**
     * 执行下一个方法的函数
     */
    then: function (res) {
      try {
        if (typeof res != undefined && res != undefined ) {
          this.log("-----return data is:----------");
          this.log(res);
        }

        /**
         * 节点内的定时器清理
         */
        if (this.sysTimeoutFunc != undefined) {
          clearTimeout(this.sysTimeoutFunc);
          this.sysTimeoutFunc = undefined;
        }


        /**
         * 判断是否需要执行上一个方法的后处理函数
         */
        var after_res = this.afterList[this.current_index - 1];
        if (typeof after_res != undefined && after_res) {
          res = after_res(res);
        }
        /**
         * 判断流程是否被停止
         */
        if (!this.get_state()) {
          this.log("when run then function, the process is stoped before");
          return res;
        }

        // 获取下一个执行方法
        var nextFunction = this.processList[this.current_index];
        //下一个方法是否存在
        var isNextFunEmptyFlag = (typeof nextFunction == undefined || nextFunction == undefined);
        /**
         * 判断是否超时
         * 1. 如果没有定时任务，则不触发超时条件
         * 2. 如果时间未过期， 则不触发超时条件
         * 3. 如果是最后一个接口，则不触发超时条件
         * 4. 反之，触发超时条件
         */
        if (typeof this.timeFunction != undefined && this.timeFunction != undefined && new Date().getTime() - this.timeBegin > this.timeout && !isNextFunEmptyFlag) {
          var time_res = this.timeFunction();
          return time_res;
        }
        this.current_index = this.current_index + 1;


        /**
         * 判断是否需要执行当前方法的预处理函数
         */
        var pre_res = this.preList[this.current_index - 1];
        if (typeof pre_res != undefined && pre_res) {
          res = pre_res(res);
        }

        /**
         * 执行下一个方法
         */
        this.log("-----call next node-----");
        if (isNextFunEmptyFlag) {
          //下一个方法未定义，则直接结束
          this.log("终极节点");
          this.set_state(false);
          this.finalize();  //执行最后的清理操作
          return res;
        } else {
          this.log("开始调用方法:" + this.processNameList[this.current_index - 1]);
          return nextFunction(res);
        }
      } catch(err) {
        this.err_catch(err);
      }
    },
    /**
     * 节点跳转
     * @param {节点名称} nodeName 
     * @param {上一个节点的返回参数/下一个节点函数的入参} res 
     */
    jumpTo: function (nodeName, res) {
      try {
        if (typeof res != undefined && res != undefined ) {
          this.log("-----return data is:----------");
          this.log(res);
        }
        this.log("call jumTo function, nextNode:" + nodeName);
        /**
         * 节点内的定时器清理
         */
        if (this.sysTimeoutFunc != undefined) {
          clearTimeout(this.sysTimeoutFunc);
          this.sysTimeoutFunc = undefined;
        }

        /**
         * 判断流程是否被停止
         */
        if (!this.get_state()) {
          this.log("when run then function, the process is stoped before");
          return res;
        }

        /**
         * 判断是否需要执行上一个方法的后处理函数
         */
        var after_res = this.afterList[this.current_index - 1];
        if (typeof after_res != undefined && after_res) {
          res = after_res(res);
        }

        for (let i = 0; this.processNameList[i] != undefined; i++) {
          if (nodeName == this.processNameList[i]) {
            this.log("fit the nodeName, index:" + i);
            this.current_index = i;
          }
        }

        // 获取下一个执行方法
        var nextFunction = this.processList[this.current_index];

        //下一个方法是否存在
        var isNextFunEmptyFlag = (typeof nextFunction == undefined || nextFunction == undefined);
        /**
         * 判断是否超时
         * 1. 如果没有定时任务，则不触发超时条件
         * 2. 如果时间未过期， 则不触发超时条件
         * 3. 如果是最后一个接口，则不触发超时条件
         * 4. 反之，触发超时条件
         */
        if (typeof this.timeFunction != undefined && this.timeFunction != undefined && new Date().getTime() - this.timeBegin > this.timeout && !isNextFunEmptyFlag) {
          var time_res = this.timeFunction();
          return time_res;
        }
        this.current_index = this.current_index + 1;


        /**
         * 判断是否需要执行当前方法的预处理函数
         */
        var pre_res = this.preList[this.current_index - 1];
        if (typeof pre_res != undefined && pre_res) {
          res = pre_res(res);
        }

        /**
         * 执行下一个方法
         */
        this.log("-----call next node-----");
        if (isNextFunEmptyFlag) {
          //下一个方法未定义，则直接结束
          this.log("终极节点");
          this.set_state(false);
          this.finalize();  //执行最后的清理操作
          return res;
        } else {
          this.log("开始调用方法:" + this.processNameList[this.current_index - 1]);
          return nextFunction(res);
        }
      } catch(err) {
        this.err_catch(err);
      }
    },
    /**
     * 执行拒绝策略
     */
    reject: function (res) {
      this.log("call reject function, err data is:------------");
      this.log(res);
      if (!this.get_state()) {
        this.log("当前流程已结束");
        return;
      }
      this.set_state(false);
      this.finalize();
      this.log("触发" + this.processNameList[this.current_index - 1] + "拒绝策略");
      var fun = this.rejectList[this.current_index - 1];
      if (typeof fun == undefined || fun == undefined) {
        return;
      } else {
        return fun(res);
      }
    },

    /**
     * reLaunch 页面跳转，同时结束流程
     */
    reLaunch: function (url) {
      this.redirectTo_type = "reLaunch";
      this.redirectTo_url = url;
      return;
    },
    /**
     * redirectTo 页面跳转，同时结束流程
     */
    redirectTo: function (url) {
      this.redirectTo_type = "redirectTo";
      this.redirectTo_url = url;
      return;
    },
    /**
     * navigateTo 页面跳转，同时结束流程
     */
    navigateTo: function (url) {
      this.redirectTo_type = "navigateTo";
      this.redirectTo_url = url;
      return;
    },
    /**
     * navigateBack 页面跳转，同时结束流程
     */
    navigateBack: function (url) {
      this.redirectTo_type = "navigateBack";
      this.redirectTo_url = url;
      return;
    },


    /**
     * 每个方法的预处理
     */
    pre: function () {
      return this;
    },

    /**
     * 每个方法的后处理
     */
    after: function () {
      return this;
    },
    /**
     * 当前方法抛出异常，执行异常处理方法
     */
    err_catch: function (res) {
      // if (!this.isRunning) {
      if (!this.get_state()) {
        return;
      }
      this.log("------《" + this.processNameList[this.current_index] + "》函数发生错误------");
      this.log(res);
      // this.isRunning = false;
      this.set_state(false);
      this.finalize();
      var errFunction = this.errList[this.current_index];
      if (typeof errFunction == undefined || errFunction == undefined) {
        if (typeof this.errDefault == undefined || this.errDefault == undefined) {
          return;
        } else {
          
          return this.errDefault(res);
        }
      } else {
        return errFunction(res);
      }
    },
    /**
     * 增加定时任务
     * @params timeFun : 超时触发的函数
     * @params tiemout : 超时时间（单位毫秒）
     *  */
    addTimer: function (timerFun, timeout) {
      this.timeFunction = timerFun
      this.timeout = timeout;
      return this;
    },

    /**
     * 获取剩余时间
     */
    getTimeoutTime(expectTime) {
      if (typeof this.timeBegin == undefined || this.timeBegin == undefined) {
        return 0;
      } else {
        var rsTime = this.timeout - (new Date().getTime() - this.timeBegin) > expectTime ? expectTime : this.timeout - (new Date().getTime() - this.timeBegin);
        return rsTime;
      }
    },
    /**
     * 调用系统的定时器，确保在一个节点函数中，也可以使用超时任务。
     * @param {定时任务} sysTimeFun 
     */
    setSysTimer: function (sysTimeFun) {
      if (this.sysTimeoutFunc != undefined) {
        clearTimeout(this.sysTimeoutFunc);
        this.sysTimeoutFunc = undefined;
      }
      this.sysTimeoutFunc = sysTimeFun;
    },
    /**
     * 增加延迟函数，用于测试使用
     * 例如：调试蓝牙的时候，如果测试连接连接蓝牙之后，发送数据的时候，蓝牙断开。
     * 则可以在连接设备函数和发送数据函数之间，增加延迟函数，使得测试人员有时间进行相应的操作
     */
    add_delay(waitTime) {
      if (waitTime == undefined) {
        waitTime = 3000;
      }
      let funcName = "延迟" + waitTime + "ms函数";
      this.add(()=>{
        setTimeout(()=>{
          this.then();
        }, waitTime);
      }, funcName);
      return this;
    }
  };
  //新建一个流程函数
  return process_info;
}

/**
 * 定义本页面，向外暴露的接口
 */
module.exports = {
  create,
  addProcess,
  getProcess,
  addFilter,
  getFilter,
}